package com.google.code.p.restrpc.client.loadbalance;

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

import com.google.code.p.restrpc.RpcRequest;

/**
 * @author dz
 */
public class RoundRobinLoadBalanceStrategy extends AbstractLoadBalanceStrategy {
    
    private AtomicInteger counter = new AtomicInteger(Integer.MAX_VALUE);
    
    @Override
    protected int doSelect(String serviceName,
                           String methodName,
                           RpcRequest[] requests) {
        if (requests == null || requests.length == 0) {
            return -1;
        }
        
        if (requests.length == 1) {
            return 0;
        }
        
        // if the counter is not initialized,initialize to the random value
        counter.compareAndSet(Integer.MAX_VALUE,
                              new Random().nextInt(requests.length));
        return counter.getAndIncrement() % requests.length;
    }
    
    public static void main(String[] args) {
        // testSerial();
        // testPariallel();
        // testCountDownLatch();
        // testGatherPoint();
    }
    
    private static void testSerial() {
        final LoadBalanceStrategy loadBalance = new RoundRobinLoadBalanceStrategy();
        final RpcRequest[] serviceNodes = new RpcRequest[4];
        
        System.out.println("TEST Case : serial");
        for (int i = 0; i < 100; i++) {
            System.out.println(i + ":"
                               + loadBalance.select("testService",
                                                    "testMethod",
                                                    serviceNodes));
        }
    }
    
    private static void testPariallel() {
        final LoadBalanceStrategy loadBalance = new RoundRobinLoadBalanceStrategy();
        final RpcRequest[] serviceNodes = new RpcRequest[4];
        
        System.out.println("TEST Case : paiallel");
        ExecutorService exec = Executors.newFixedThreadPool(20);
        final Semaphore semp = new Semaphore(10);
        for (int i = 0; i < 100; i++) {
            final int sequence = i;
            exec.execute(new Runnable() {
                public void run() {
                    try {
                        semp.acquire();
                        System.out.println("Thread:" + sequence
                                           + " | selected:"
                                           + loadBalance.select("testService",
                                                                "testMethod",
                                                                serviceNodes));
                        semp.release();
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        exec.shutdown();
        
    }
    
    private static void testCountDownLatch() {
        final LoadBalanceStrategy loadBalance = new RoundRobinLoadBalanceStrategy();
        final RpcRequest[] serviceNodes = new RpcRequest[4];
        
        System.out.println("TEST Case : CountDownLatch");
        
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        
        for (int i = 0; i < 100; i++) {
            final int sequence = i;
            new Thread(new Runnable() {
                public void run() {
                    try {
                        System.out.println("Thread " + sequence
                                           + " waiting select....");
                        countDownLatch.await();
                        System.out.println("Thread " + sequence
                                           + " | selected:"
                                           + loadBalance.select("testService",
                                                                "testMethod",
                                                                serviceNodes));
                    }
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        try {
            Thread.currentThread().sleep(1000);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        countDownLatch.countDown();
    }
    
    private static void testGatherPoint() {
        final LoadBalanceStrategy loadBalance = new RoundRobinLoadBalanceStrategy();
        final RpcRequest[] serviceNodes = new RpcRequest[4];
        
        System.out.println("TEST Case : CyclicBarrier");
        
        final CyclicBarrier barrier = new CyclicBarrier(10, new Runnable() {
            public void run() {
                System.out.println("selected:" + loadBalance.select("testService",
                                                                    "testMethod",
                                                                    serviceNodes));
            }
        });
        
        ExecutorService execPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            execPool.execute(new Runnable() {
                public void run() {
                    try {
                        barrier.await();
                    }
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        execPool.shutdown();
    }
}
